package com.googlecode.restitory.api.servlet;

import java.io.BufferedReader;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.googlecode.restitory.api.ConflictException;
import com.googlecode.restitory.api.JsonHelper;
import com.googlecode.restitory.api.JsonService;
import com.googlecode.restitory.gae.filter.util.JsonUtil;

public class JsonServlet extends HttpServlet {

	private static final long serialVersionUID = -4139667332531565175L;

	private static final Logger LOG = LoggerFactory
			.getLogger(JsonServlet.class);

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		try {
			String query = req.getParameter("query");
			if (query != null) {
				query(req, resp);
				return;
			}
			String count = req.getParameter("count");
			if (count != null) {
				count(req, resp);
				return;
			}
			super.service(req, resp);
		} catch (ConflictException e) {
			LOG.warn("Conflict", e);
			resp.sendError(409);
		}
	}

	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		JsonService service = getJsonService();
		String path = req.getRequestURI();
		String rev = req.getParameter("_rev");
		if (rev != null) {
			service.delete(path, Long.parseLong(rev));
		} else {
			service.delete(path);
		}
		ServletHelper.write(resp, path);
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		JsonService service = getJsonService();
		JsonObject json = service.get(req.getRequestURI());
		if (json == null) {
			resp.sendError(404);
			return;
		}
		ServletHelper.write(resp, json);
	}

	private void query(HttpServletRequest req, HttpServletResponse resp) {
		String json = req.getParameter("query");
		JsonObject query;
		if ("true".equals(json)) {
			query = readBody(req).getAsJsonObject();
		} else {
			query = (JsonObject) JsonHelper.parse(json);
		}
		query.addProperty("kind", req.getRequestURI());
		JsonService service = getJsonService();
		if (JsonUtil.t(query.get("cursor"))) {
			ServletHelper.write(resp, service.cursor(query));
		} else {
			ServletHelper.write(resp, service.query(query));
		}
	}

	private void count(HttpServletRequest req, HttpServletResponse resp) {
		String json = req.getParameter("count");
		JsonObject query;
		if ("true".equals(json)) {
			query = readBody(req).getAsJsonObject();
		} else {
			query = (JsonObject) JsonHelper.parse(json);
		}
		query.addProperty("kind", req.getRequestURI());
		JsonService service = getJsonService();
		ServletHelper.write(resp, service.count(query));
	}

	@Override
	protected void doPut(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		JsonObject json = readBody(req).getAsJsonObject();
		JsonService service = getJsonService();
		String path = service.put(json);
		ServletHelper.write(resp, path);
	}

	private static JsonService getJsonService() {
		return JsonServiceFilter.get();
	}

	public static JsonElement readBody(HttpServletRequest req) {
		try {
			checkContentType(req);
			BufferedReader reader = req.getReader();
			JsonElement json = JsonHelper.parse(reader);
			if (json.isJsonObject()) {
				JsonObject obj = (JsonObject) json;
				obj.addProperty("_self", req.getRequestURI());
			}
			return json;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public static void checkContentType(HttpServletRequest req) {
		String contentType = ServletHelper.getMediaType(req.getContentType());
		if (contentType != null && !contentType.equals("application/json")) {
			throw new RuntimeException("Content-Type must be application/json");
		}
		String charset = req.getCharacterEncoding();
		if (charset == null) {
			throw new RuntimeException("charset is required in Content-Type");
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		JsonObject json = readBody(req).getAsJsonObject();
		JsonService service = getJsonService();
		String path = service.post(json);
		ServletHelper.write(resp, path);
	}

}
